home *** CD-ROM | disk | FTP | other *** search
/ ADA Programming Guide / ADA Programming Guide.iso / ada_tut / ada_tutr.ada < prev    next >
Text File  |  1996-01-30  |  26KB  |  520 lines

  1. -- ADA_TUTR.ADA   Ver. 3.00   22-AUG-1994   Copyright 1988-1994 John J. Herro
  2. -- Software Innovations Technology
  3. -- 1083 Mandarin Drive NE, Palm Bay, FL  32905-4706   (407)951-0233
  4. --
  5. -- Before compiling this file, you must compile ONE of the following:
  6. --
  7. --    JANUS16.PKG   Recommended when using a PC with 16-bit Janus/Ada.
  8. --    JANUS32.PKG   Recommended when using a PC with 32-bit Janus/Ada.
  9. --    MERIDIAN.ADA  Recommended when using a PC with a Meridian Ada compiler.
  10. --    UNIX.ADA      Recommended for UNIX based systems, if you can also
  11. --                     compile ONECHAR.C or ALTCHAR.C with a C compiler and
  12. --                     link with Ada.
  13. --    VAX.ADA       Recommended when using VAX Ada.
  14. --    VANILLA.ADA   "Plain vanilla" version for all other systems.  Should work
  15. --                     with ANY standard Ada compiler.  On some systems,
  16. --                     VANILLA.ADA may require you to strike ENTER after each
  17. --                     response.  However, you don't have to strike ENTER with
  18. --                     recent versions of TeleGen Ada by Telesoft.
  19. --
  20. -- See the PRINT.ME file for more information on installing ADA-TUTR on other
  21. -- computers.
  22. --
  23. --
  24. -- Before Running ADA-TUTR on a PC:
  25. --
  26. -- ADA-TUTR uses ANSI escape sequences for highlighting, cursor positioning,
  27. -- reverse video, etc.  Before ADA-TUTR will work correctly on a PC, you must
  28. -- install the device driver ANSI.SYS, which came with your copy of DOS.  To
  29. -- install ANSI.SYS, do the following:
  30. --
  31. -- 1.  If there's a file CONFIG.SYS in the root directory of the disk from
  32. --     which you boot, type it and look for a line saying "DEVICE=ANSI.SYS"
  33. --     (without the quotes), in either upper or lower case.  If that line isn't
  34. --     present, add it to CONFIG.SYS anywhere in the file, using an ordinary
  35. --     text editor or word processor in the non-document mode.  If there's no
  36. --     CONFIG.SYS file, create one containing the single line "DEVICE=ANSI.SYS"
  37. --     (without the quotes).
  38. --
  39. -- 2.  If there's no file ANSI.SYS in your root directory, copy ANSI.SYS from
  40. --     your DOS distribution diskette to the root directory of the disk from
  41. --     which you boot.
  42. --
  43. -- 3.  Reboot the computer.  ADA-TUTR should then work correctly.
  44. --
  45.  
  46. -- Introduction:
  47. --
  48. -- ADA-TUTR provides interactive instruction in the Ada programming language,
  49. -- allowing you to learn at your own pace.  On a PC, access to an Ada compiler
  50. -- is helpful, but not required.  You can exit this program at any time by
  51. -- striking X, and later resume the session exactly where you left off.  If you
  52. -- have a color monitor, you can set the foreground, background, and border
  53. -- colors at any time by typing S.
  54. --
  55. -- ADA-TUTR presents a screenful of information at a time.  Screens are read
  56. -- in 64-byte blocks from the random access file ADA_TUTR.DAT, using Direct_IO.
  57. -- For most screens, ADA-TUTR waits for you to strike one character to
  58. -- determine which screen to show next.  Screens are numbered starting with
  59. -- 101; each screen has a three-digit number.  Screens 101 through 108 have
  60. -- special uses, as follows:
  61. --
  62. -- 101 - This screen is presented when you complete the Ada course.  It
  63. --       contains a congratulatory message.  After this screen is shown,
  64. --       control returns directly to the operating system; the program doesn't
  65. --       wait for you to strike a character.
  66. -- 102 - This screen is presented when you exit ADA-TUTR before completing the
  67. --       course.  After this screen is shown, control returns directly to the
  68. --       operating system; the program doesn't wait for you to strike a
  69. --       character.
  70. -- 103 - This screen is shown whenever you strike X.  It displays the number of
  71. --       the last screen shown and the approximate percentage through the
  72. --       course.  It then asks if you want to exit the program.  If you strike
  73. --       Y, screen 102 is shown and control returns to the operating system.
  74. --       If you type N, screen 108 is shown to provide a menu of further
  75. --       choices.  From screen 103, you can also strike M to see the main menu
  76. --       (screen 106).
  77. -- 104 - This is the opening screen.  It asks if you've used ADA-TUTR before.
  78. --       If you strike N, a welcome screen is presented and the course begins.
  79. --       If you strike Y, screen 107 is shown.
  80. -- 105 - This screen allows you to type the number of the next screen you want
  81. --       to see.  For this screen, instead of striking one character, you type
  82. --       a three-digit number and presses ENTER.  Any number from 104 through
  83. --       the largest screen number is accepted.
  84. -- 106 - This screen contains the main menu of topics covered in ADA-TUTR.
  85. --       When you select a main topic, an appropriate sub-menu is shown.
  86. -- 107 - This screen is shown when you say that you've used ADA-TUTR before.
  87. --       It says "Welcome back!" and provides a menu that lets you resume where
  88. --       you left off, go back to the last question or Outside Assignment, go
  89. --       to the main menu (screen 106), or go to any specified screen number
  90. --       (via screen 105).
  91. -- 108 - This screen is shown when you answer N to screen 103.  It provides a
  92. --       menu similar to screen 107, except that the first choice takes you
  93. --       back to the screen shown before you saw 103.  For example, if you
  94. --       strike X while viewing screen 300, you'll see screen 103.  If you then
  95. --       answer N, you'll see screen 108.  From 108 the first menu selection
  96. --       takes you back to 300.
  97. --
  98.  
  99. -- Format of the Data File:
  100. --
  101. -- ADA-TUTR.DAT is a random access file of 64-byte blocks.  The format of this
  102. -- file changed considerably with version 2.00 of ADA-TUTR.  It's now much more
  103. -- compact, and, although it's still a data file, it now contains only the 95
  104. -- printable ASCII characters.
  105. --
  106. -- The first block of ADA_TUTR.DAT is referred to as block 1, and the first 35
  107. -- blocks together are called the index.  Bytes 2 through 4 of block 1 contain,
  108. -- in ASCII, the number of the welcome screen that's shown when you say that
  109. -- you haven't used ADA-TUTR before.  Bytes 6 through 8 of block 1 contain the
  110. -- number of the highest screen in the course.  (Bytes 1 and 5 of block 1
  111. -- contain spaces.)
  112. --
  113. -- Bytes 9 of block 1 through the end of block 31 contain four bytes of
  114. -- information for each of the possible screens 101 through 658.  For example,
  115. -- information for screen 101 is stored in bytes 9 through 12 of block 1, the
  116. -- next four bytes are for screen 102, etc.  For screens that don't exist, all
  117. -- four bytes contain spaces.
  118. --
  119. -- The first of the four bytes is A if the corresponding screen introduces an
  120. -- Outside Assignment, Q if the screen asks a question, or a space otherwise.
  121. -- The next two bytes give the number of the block where data for the screen
  122. -- begins, in base 95!  A space represents 0, ! represents 1, " represents 2,
  123. -- # represents 3, $ represents 4, etc., through all the printable characters
  124. -- of the ASCII set.  A tilde (~) represents 94.
  125. --
  126. -- The last of the four bytes gives the position, 1 through 64, within the
  127. -- block where the data for this screen starts.  Again, ! represents 1,
  128. -- " represents 2, # represents 3, etc.
  129. --
  130. -- Data for the screens are stored starting in position 1 of block 36.  In the
  131. -- screen data, the following characters have special meaning:
  132. --
  133. --           %  turns on high intensity.
  134. --           @  displays the number of spaces indicated by the next
  135. --                 character (# represents 3, $ represents 4, etc.)
  136. --           \  turns on reverse video and leaves one space.
  137. --           ^  turns on high intensity and leaves one space.
  138. --           `  restores normal video.
  139. --           {  causes CR-LF.
  140. --           }  moves cursor to row 24, column 1, for a prompt.
  141. --           ~  restores normal video and leaves one space.
  142. --
  143. -- These characters have special meaning in screen 103 only:
  144. --
  145. --           #  shows approximate percentage through the course.
  146. --           $  shows the number of the screen seen before 103.
  147. --
  148. -- Immediately after }, b represents "Please type a space to go on, or B to go
  149. -- back." and q represents "Please type a space to go on, or B or Q to go back
  150. -- to the question."
  151. --
  152.  
  153. --
  154. -- The data for each screen is followed by the "control information" for that
  155. -- screen, in square brackets.  The control information is a list of characters
  156. -- that you might strike after seeing this screen.  Each character is followed
  157. -- by the three-digit number of the next screen to be shown when that character
  158. -- is struck.  For example, Y107N122 is the control information for screen 104.
  159. -- This means that if you strike Y, screen 107 will be shown next, and if you
  160. -- strikes N, screen 122 will be shown.  Striking any other character will
  161. -- simply cause a beep (except that X can always be typed to exit the program,
  162. -- S can always be typed to set colors, and CR will be ignored).  If the
  163. -- control information is simply #, you are prompted to type the next screen
  164. -- number.  This feature is used in screen 105.
  165. --
  166. -- A "screen number" of 098 following a character means "go back to the last
  167. -- Outside Assignment," and 099 means "go back to the last question."  These
  168. -- special numbers are used in screens 107 and 108.  Number 100 means "go back
  169. -- to the previous screen seen."
  170. --
  171. -- ADA-TUTR opens the Data File in In_File mode for read-only access.
  172. --
  173. --
  174. --
  175. -- Format of the User File:
  176. --
  177. -- The User File ADA_TUTR.USR initially doesn't exist.  It's created the first
  178. -- time ADA-TUTR is run.
  179. --
  180. -- ADA_TUTR.USR is a random access file containing one 64-byte block.  Bytes 2
  181. -- through 4 contain, in ASCII, the number of the last screen read the last
  182. -- time you ran ADA-TUTR.  Byte 6 contains a digit for the foreground color you
  183. -- select, byte 8 contains a digit for the background color, and byte 10
  184. -- contains a digit for the border color.  All other bytes contain spaces.  The
  185. -- ASCII characters '0' through '7' represent black, red, green, yellow, blue,
  186. -- magenta, cyan, and white, in that order.  Note that not all color PCs have a
  187. -- separate border color.  ADA_TUTR.USR is a random access file so that it can
  188. -- be easily updated by Ada.  It contains 64 bytes so that it can be accessed
  189. -- with the same package, namely Random_IO, that accesses the Data File.
  190. --
  191. -- If the User File exists, ADA-TUTR opens it in Inout_File mode for read/write
  192. -- access.  If it doesn't exist, ADA-TUTR creates it.
  193. --
  194.  
  195. with Custom_IO, Direct_IO; use Custom_IO;
  196. procedure Ada_Tutr is
  197.    subtype Block_Subtype is String(1 .. 64);
  198.    package Random_IO is new Direct_IO(Block_Subtype); use Random_IO;
  199.    IxSize      : constant := 35;              -- Number of blocks in the index.
  200.    Data_File   : File_Type;            -- The file from which screens are read.
  201.    User_File   : File_Type;          -- Remembers last screen seen, and colors.
  202.    Block       : Block_Subtype;                -- Buffer for random-access I/O.
  203.    Vpos        : Integer;                       -- Number of the current block.
  204.    Hpos        : Integer;             -- Current position within current block.
  205.    SN, Old_SN  : Integer := 104;        -- Screen num. and previous screen num.
  206.    Quitting_SN : Integer := 104;           -- Screen number where you left off.
  207.    Highest_SN  : Integer;               -- Highest screen number in the course.
  208.    Welcome_SN  : Integer;           -- Number of the screen shown to new users.
  209.    Indx        : String(1 .. 64*IxSize);           -- Index from the Data File.
  210.    Files_OK    : Boolean := False;        -- True when files open successfully.
  211.    Legal_Note  : constant String := " Copyright 1988-94 John J. Herro ";
  212.                        -- Legal_Note isn't used by the program, but it causes
  213.                        -- most compilers to place this string in the .EXE file.
  214.    procedure Open_Data_File is separate;
  215.    procedure Open_User_File is separate;
  216.    procedure Show_Current_Screen is separate;
  217.    procedure Get_Next_Screen_Number is separate;
  218. begin
  219.    Open_Data_File;
  220.    Open_User_File;
  221.    if Files_OK then
  222.       Set_Border_Color(To => Border_Color);              -- Set default colors.
  223.       Put(Normal_Colors);
  224.       while SN > 0 loop          -- "Screen number" of 0 means end the program.
  225.          Put(Clear_Scrn);                                  -- Clear the screen.
  226.          Show_Current_Screen;
  227.          Get_Next_Screen_Number;
  228.       end loop;
  229.       Block := (others => ' ');       -- Write user-specific data to user file.
  230.       Block(1 .. 4) := Integer'Image(Quitting_SN);
  231.       Block(6)  := Fore_Color_Digit;
  232.       Block(8)  := Back_Color_Digit;
  233.       Block(10) := Character'Val(Color'Pos(Border_Color) + 48);
  234.       Write(User_File, Item => Block, To => 1);
  235.       Close(Data_File);
  236.       Close(User_File);
  237.    end if;
  238. end Ada_Tutr;
  239.  
  240. separate (Ada_Tutr)
  241. procedure Open_Data_File is
  242.    Data_File_Name : constant String := "ADA_TUTR.DAT";
  243. begin
  244.    Open(Data_File, Mode => In_File, Name => Data_File_Name);
  245.    for I in 1 .. IxSize loop             -- Read index from start of Data File.
  246.       Read(Data_File, Item => Block, From => Count(I));
  247.       Indx(64*I - 63 .. 64*I) := Block;
  248.    end loop;
  249.    Welcome_SN := Integer'Value(Indx(2 .. 4));
  250.    Highest_SN := Integer'Value(Indx(6 .. 8));
  251.    Files_OK := True;
  252. exception
  253.    when Name_Error =>
  254.       Put("I'm sorry.  The file " & DATA_FILE_NAME);
  255.       Put_Line(" seems to be missing.");
  256.    when others =>
  257.       Put("I'm sorry.  The file " & DATA_FILE_NAME);
  258.       Put_Line(" seems to have the wrong form.");
  259. end Open_Data_File;
  260.  
  261.  
  262.  
  263. separate (Ada_Tutr)
  264. procedure Open_User_File is
  265.    User_File_Name : constant String := "ADA_TUTR.USR";
  266. begin
  267.    Open(User_File, Mode => Inout_File, Name => User_File_Name);
  268.    Read(User_File, Item => Block, From => 1);
  269.    Quitting_SN      := Integer'Value(Block(1 .. 4));
  270.    Old_SN           := Quitting_SN;
  271.    Foregrnd_Color   := Color'Val(Integer'Value(Block(5 .. 6)));
  272.    Backgrnd_Color   := Color'Val(Integer'Value(Block(7 .. 8)));
  273.    Border_Color     := Color'Val(Integer'Value(Block(9 .. 10)));
  274.    Fore_Color_Digit := Block(6);
  275.    Back_Color_Digit := Block(8);
  276.    Normal_Colors(6) := Fore_Color_Digit;
  277.    Normal_Colors(9) := Back_Color_Digit;
  278. exception
  279.    when Name_Error =>
  280.       begin
  281.          Create(User_File, Mode => Inout_File, Name => User_File_Name);
  282.       exception
  283.          when others =>
  284.             Put("I'm sorry.  I couldn't find or create ");
  285.             Put_Line(User_File_Name);
  286.             Files_OK := False;
  287.       end;
  288.    when others =>
  289.       Put_Line("I'm sorry.  The file " & USER_FILE_NAME & " seems to have");
  290.       Put_Line("the wrong form or contain bad data.");
  291.       Put_Line("You might want to delete the file and try again.");
  292.       Put_Line("(Default values will be used.)");
  293.       Files_OK := False;
  294. end Open_User_File;
  295.  
  296. separate (Ada_Tutr)
  297. procedure Show_Current_Screen is
  298.    Half_Diff : Integer := (Highest_SN - Welcome_SN) / 2;
  299.    Percent   : Integer := (50 * (Old_SN - Welcome_SN)) / Half_Diff;
  300.                           -- Percentage of the course completed.  Using 50 and
  301.                           -- Half_Diff guarantees that the numerator < 2 ** 15.
  302.    Expanding : Boolean := False;        -- True when expanding multiple spaces.
  303.    Prompting : Boolean := False;       -- True for first character in a prompt.
  304.    Space     : constant String(1 .. 80) := (others => ' ');
  305.    procedure Process_Char is separate;
  306. begin
  307.    Vpos := 95*(Character'Pos(Indx(SN*4 - 394)) - 32) +        -- Point to start
  308.                Character'Pos(Indx(SN*4 - 393)) - 32;          -- of current
  309.    Hpos := Character'Pos(Indx(SN*4 - 392)) - 32;              -- screen.
  310.    Read(Data_File, Item => Block, From => Count(Vpos));
  311.    if Percent < 0 then                      -- Make sure Percent is reasonable.
  312.       Percent := 0;
  313.    elsif Percent > 99 then
  314.       Percent := 99;
  315.    end if;
  316.    while Block(Hpos) /= '[' or Expanding loop     -- [ starts the control info.
  317.       if Expanding then
  318.          Put(Space(1 .. Character'Pos(Block(Hpos)) - 32));
  319.          Expanding := False;
  320.       elsif Prompting then
  321.          case Block(Hpos) is
  322.             when 'b' => Put("Please type a space to go on, or B to go back.");
  323.             when 'q' => Put("Please type a space to go on, or B or Q to go ");
  324.                         Put("back to the question.");
  325.             when others => Process_Char;
  326.          end case;
  327.          Prompting := False;
  328.       else
  329.          Process_Char;
  330.       end if;
  331.       Hpos := Hpos + 1;
  332.       if Hpos > Block'Length then
  333.          Vpos := Vpos + 1;
  334.          Hpos := 1;
  335.          Read(Data_file, Item => Block, From => Count(Vpos));
  336.       end if;
  337.    end loop;
  338. end Show_Current_Screen;
  339.  
  340. separate (Ada_Tutr.Show_Current_Screen)
  341. procedure Process_Char is
  342. begin
  343.    case Block(Hpos) is
  344.       when '{'    => New_Line;                           -- { = CR-LF.
  345.       when '@'    => Expanding := True;                  -- @ = several spaces.
  346.       when '^'    => Put(ASCII.ESC & "[1m ");            -- ^ = bright + space.
  347.       when '~'    => Put(Normal_Colors & ' ');           -- ~ = normal + space.
  348.       when '%'    => Put(ASCII.ESC & "[1m");             -- % = bright.
  349.       when '`'    => Put(Normal_Colors);                 -- ` = normal.
  350.       when '}'    => Put(ASCII.ESC & "[24;1H");          -- } = go to line 24.
  351.                      Prompting := True;
  352.       when '\'    => Put(ASCII.ESC & "[7m ");            -- \ = rev. vid. + sp.
  353.       when '$'    => if SN = 103 then                    -- $ = screen #.
  354.                         Put(Integer'Image(Old_SN));
  355.                      else
  356.                         Put('$');
  357.                      end if;
  358.       when '#'    => if SN = 103 then                    -- # = % completed.
  359.                         Put(Integer'Image(Percent));
  360.                      else
  361.                         Put('#');
  362.                      end if;
  363.       when others => Put(Block(Hpos));
  364.    end case;
  365. end Process_Char;
  366.  
  367. separate (Ada_Tutr)
  368. procedure Get_Next_Screen_Number is
  369.    Ctrl_Info : Block_Subtype;          -- Control info. for the current screen.
  370.    Place     : Integer := 1;              -- Current position within Ctrl_Info.
  371.    Input     : String(1 .. 4);                  -- Screen number that you type.
  372.    Len       : Integer;                            -- Length of typed response.
  373.    Valid     : Boolean;                   -- True when typed response is valid.
  374.    procedure Set_Colors is separate;
  375.    procedure Input_One_Keystroke is separate;
  376. begin
  377.    while Block(Hpos) /= ']' loop    -- Read control information from Data File.
  378.       Hpos := Hpos + 1;
  379.       if Hpos > Block'Length then
  380.          Vpos := Vpos + 1;
  381.          Hpos := 1;
  382.          Read(Data_File, Item => Block, From => Count(Vpos));
  383.       end if;
  384.       Ctrl_Info(Place) := Block(Hpos);
  385.       Place := Place + 1;
  386.    end loop;
  387.    if SN = 103 then                    -- Screen 103 means you typed X to exit.
  388.       Quitting_SN := Old_SN;
  389.    elsif SN >= Welcome_SN then              -- Save SN so you can return to it.
  390.       Old_SN := SN;
  391.    end if;
  392.    if SN < 103 then                          -- Set SN to # of the next screen.
  393.       SN := 0;      -- Set signal to end the program after screens 101 and 102.
  394.    elsif Ctrl_Info(1) = '#' then            -- You type the next screen number.
  395.       Valid := False;
  396.       while not Valid loop              -- Keep trying until response is valid.
  397.          Put("# ");                                -- Prompt for screen number.
  398.          Input := "    ";  Get_Line(Input, Len);        -- Input screen number.
  399.          if Input(1) = 'x' or Input(1) = 'X' or Input(1) = ASCII.ETX then
  400.             SN := 103;                        -- Show screen 103 if you type X.
  401.             Valid := True;                            -- X is a valid response.
  402.          elsif Input(1) = 's' or Input(1) = 'S' then
  403.             Set_Colors;                            -- Set colors if you type S.
  404.             Valid := True;                            -- S is a valid response.
  405.          else
  406.             begin                                    -- Convert ASCII input to
  407.                SN := Integer'Value(Input);           -- integer.  If in range,
  408.                Valid := SN in 104 .. Highest_SN;     -- set Valid to True.  If
  409.             exception                                -- it can't be converted
  410.                when others => null;                  -- (e.g., illegal char.),
  411.             end;                                     -- or it's out of range,
  412.          end if;                                     -- leave Valid = False so
  413.          if not Valid and Len > 0 then               -- you can try again.
  414.             Put_Line("Incorrect number.  Please try again.");
  415.          end if;
  416.       end loop;
  417.    else
  418.       Input_One_Keystroke;
  419.    end if;
  420. end Get_Next_Screen_Number;
  421.  
  422. separate (Ada_Tutr.Get_Next_Screen_Number)
  423. procedure Set_Colors is
  424.    Bright    : constant String := ASCII.ESC & "[1m";  -- Causes high intensity.
  425.    Keystroke : Character := 'f';             -- Single character that you type.
  426.    Space     : constant String(1 .. 23) := (others => ' ');
  427. begin
  428.    while Keystroke = 'f' or Keystroke = 'b' or Keystroke = 'r' or
  429.          Keystroke = 'F' or Keystroke = 'B' or Keystroke = 'R' loop
  430.       Put(Clear_Scrn);                                     -- Clear the screen.
  431.       New_Line;
  432.       Put(Space & "The " & Bright & "foreground" & Normal_Colors);
  433.       Put_Line(" color is now " & Color'Image(Foregrnd_Color) & '.');
  434.       Put(Space & "The " & Bright & "background" & Normal_Colors);
  435.       Put_Line(" color is now " & Color'Image(Backgrnd_Color) & '.');
  436.       Put(Space & "The " & Bright & "  border  " & Normal_Colors);
  437.       Put_Line(" color is now " & Color'Image(Border_Color) & '.');
  438.       New_Line;
  439.       Put_Line(Space & " Note:  Some color PCs don't have");
  440.       Put_Line(Space & "     separate border colors.");
  441.       New_Line;
  442.       Put_Line(Space & "             Strike:");
  443.       Put_Line(Space & "F to change the foreground color,");
  444.       Put_Line(Space & "B to change the background color,");
  445.       Put_Line(Space & "R to change the   border   color.");
  446.       New_Line;
  447.       Put_Line(Space & "Strike any other key to continue.");
  448.       Get(Keystroke);                       -- Get one character from keyboard.
  449.       if Keystroke = 'f' or Keystroke = 'F' then
  450.          Foregrnd_Color := Color'Val((Color'Pos(Foregrnd_Color) + 1) mod 8);
  451.          if Foregrnd_Color = Backgrnd_Color then
  452.             Foregrnd_Color := Color'Val((Color'Pos(Foregrnd_Color) + 1) mod 8);
  453.          end if;
  454.       elsif Keystroke = 'b' or Keystroke = 'B' then
  455.          Backgrnd_Color := Color'Val((Color'Pos(Backgrnd_Color) + 1) mod 8);
  456.          if Foregrnd_Color = Backgrnd_Color then
  457.             Backgrnd_Color := Color'Val((Color'Pos(Backgrnd_Color) + 1) mod 8);
  458.          end if;
  459.       elsif Keystroke = 'r' or Keystroke = 'R' then
  460.          Border_Color := Color'Val((Color'Pos(Border_Color) + 1) mod 8);
  461.       end if;
  462.       Fore_Color_Digit := Character'Val(48 + Color'Pos(Foregrnd_Color));
  463.       Back_Color_Digit := Character'Val(48 + Color'Pos(Backgrnd_Color));
  464.       Normal_Colors(6) := Fore_Color_Digit;
  465.       Normal_Colors(9) := Back_Color_Digit;
  466.       Put(Normal_Colors);
  467.       Set_Border_Color(To => Border_Color);
  468.    end loop;
  469. end Set_Colors;
  470.  
  471. separate (Ada_Tutr.Get_Next_Screen_Number)
  472. procedure Input_One_Keystroke is
  473.    Keystroke : Character;                    -- Single character that you type.
  474.    Valid     : Boolean := False;          -- True when typed response is valid.
  475.    Where     : Integer;              -- Location of control block in Data File.
  476.    Search    : Character;    -- 'A' = last Outside Assignment; 'Q' = last Ques.
  477. begin
  478.    Put("  >");                                     -- Prompt for one character.
  479.    while not Valid loop                 -- Keep trying until response is valid.
  480.       Get(Keystroke);                       -- Get one character from keyboard.
  481.       if Keystroke in 'a' .. 'z' then          -- Force upper case to simplify.
  482.          Keystroke := Character'Val(Character'Pos(Keystroke) - 32);
  483.       end if;
  484.       if Keystroke = 'X' or Keystroke = ASCII.ETX then
  485.          SN := 103;                           -- Show screen 103 if you type X.
  486.          Valid := True;                               -- X is a valid response.
  487.       elsif Keystroke = 'S' then
  488.          Set_Colors;                               -- Set colors if you type S.
  489.          Valid := True;                               -- S is a valid response.
  490.       end if;
  491.       Place := 1;           -- Search list of valid characters for this screen.
  492.       Valid := Valid;             -- This statement works around a minor bug in
  493.                                   -- ver. 1.0 of the Meridian IFORM optimizer.
  494.       while not Valid and Ctrl_Info(Place) /= ']' loop      -- ] ends the list.
  495.          if Keystroke = Ctrl_Info(Place) then
  496.                   -- Typed char. found in list; get screen # from control info.
  497.             SN := Integer'Value(Ctrl_Info(Place + 1 .. Place + 3));
  498.             Valid := True;   -- Characters in the list are all valid responses.
  499.          end if;
  500.          Place := Place + 4;    -- A 3-digit number follows each char. in list.
  501.       end loop;
  502.       if not Valid and Keystroke /= ASCII.CR then        -- Beep if response is
  503.          Put(ASCII.BEL);                                 -- not valid, but
  504.       end if;                                            -- ignore CRs quietly.
  505.    end loop;
  506.    if SN = 98 then                       -- Go back to last Outside Assignment.
  507.       Search := 'A';
  508.    elsif SN = 99 then                              -- Go back to last question.
  509.       Search := 'Q';
  510.    elsif SN = 100 then                      -- Go back to the last screen seen.
  511.       SN := Quitting_SN;
  512.    end if;
  513.    if SN = 98 or SN = 99 then
  514.       SN := Old_SN;
  515.       while SN > Welcome_SN and Indx(SN*4 - 395) /= Search loop
  516.          SN := SN - 1;
  517.       end loop;
  518.    end if;
  519. end Input_One_Keystroke;
  520.